--
-- Author:zhangxuri
-- Date:2021-05-20
--


local url = require "socket.url"
local http = require "resty.http"
local table_clear = require "table.clear"
local cjson_safe = require "cjson.safe"


local kong = kong
local ngx = ngx
local encode_base64 = ngx.encode_base64
local tostring = tostring
local tonumber = tonumber

local DEFAULT_HTTP_ENDPOINT = "127.0.0.1"
local DEFAULT_METHOD = "GET"
local DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded"
local DEFAULT_TIMEOUT = 10000
local DEFAULT_KEEPALIVE = 80000
local DEFAULT_RETRY_COUNT = 1

local parsed_urls_cache = {}
local headers_cache = {}
local params_cache = {
  headers = headers_cache,
}


--- 解析url
local function parse_url(host_url)
  local parsed_url = parsed_urls_cache[host_url]

  if parsed_url then
    return parsed_url
  end

  parsed_url = url.parse(host_url)
  if not parsed_url.port then -- http、https请求默认端口
    if parsed_url.scheme == "http" then
      parsed_url.port = 80
    elseif parsed_url.scheme == "https" then
      parsed_url.port = 443
    end
  end
  if not parsed_url.path then -- 默认path
    parsed_url.path = "/"
  end

  parsed_urls_cache[host_url] = parsed_url -- 缓存解析结果

  return parsed_url
end


--- 发送报文
local function send_payload(http_endpoint, method, payload, content_type)

  local method = method or DEFAULT_METHOD
  local timeout = DEFAULT_TIMEOUT
  local keepalive = DEFAULT_KEEPALIVE
  local content_type = content_type or DEFAULT_CONTENT_TYPE
  local http_endpoint = http_endpoint or DEFAULT_HTTP_ENDPOINT

  local ok, err
  local parsed_url = parse_url(http_endpoint) -- 解析目标url
  local host = parsed_url.host -- 主机
  local port = tonumber(parsed_url.port) -- 端口

  local httpc = http.new() -- 新建http请求
  httpc:set_timeout(timeout) -- 超时
  ok, err = httpc:connect(host, port) -- 建立连接
  if not ok then
    return nil, "failed to connect to " .. host .. ":" .. tostring(port) .. ": " .. err
  end

  if parsed_url.scheme == "https" then -- 验证https握手
    local _, err = httpc:ssl_handshake(true, host, false)
    if err then
      return nil, "failed to do SSL handshake with " ..
        host .. ":" .. tostring(port) .. ": " .. err
    end
  end

  table_clear(headers_cache) -- 清空header缓存表

  -- 缓存请求头
  headers_cache["Host"] = parsed_url.host -- 主机
  headers_cache["Content-Type"] = content_type -- 类型
  headers_cache["Content-Length"] = #payload -- 长度
  if parsed_url.userinfo then -- basic认证信息
    headers_cache["Authorization"] = "Basic " .. encode_base64(parsed_url.userinfo)
  end

  -- 缓存请求参数
  params_cache.method = method -- 方法
  params_cache.path = parsed_url.path -- path
  params_cache.query = parsed_url.query -- 参数
  params_cache.body = payload -- 请求体

  local res, err = httpc:request(params_cache) -- 发送请求
  if not res then
    return nil, "failed request to " .. host .. ":" .. tostring(port) .. ": " .. err
  end

  local response_body = res:read_body() -- 读响应体

  local success = res.status == 200
  local msg

  if success then
    local response_body_table = cjson_safe.decode(response_body)
    if response_body_table then
      success = response_body_table.code >= 200 and response_body_table.code < 400  -- 返回码小于400认为成功
      msg = response_body_table.message
    else
      msg = response_body or "响应体无内容."
    end
  else
    msg = "request to " .. host .. ":" .. tostring(port) ..
      " returned status code " .. tostring(res.status) .. " and body " ..
      response_body
  end

  ok, err = httpc:set_keepalive(keepalive) -- 链接保持
  if not ok then
    kong.log.err("failed keepalive for ", host, ":", tostring(port), ": ", err)
  end

  return success, msg
end


--- 发送http请求
local function send_http_request(http_endpoint, method, body, content_type)

  local http_endpoint = http_endpoint
  local method = method
  local payload = body
  local content_type = content_type
  local retry_times = 1

  local ok, msg = send_payload(http_endpoint, method, payload, content_type)

  -- local msg = ""

  -- while not ok and retry_times < DEFAULT_RETRY_COUNT do
  --   retry_times = retry_times + 1
  --   msg = msg .. "请求发送失败:" .. err .. "||进行第" .. retry_times .. "/".. DEFAULT_RETRY_COUNT .. "次重试。\n"
  --   ok, err = send_payload(http_endpoint, method, payload, content_type)
  -- end

  -- if not ok then
  --   msg = msg .. "请求发送失败:" .. err .. "||进行了" .. retry_times .. "次重试后仍未成功。\n"
  --   return ok, msg
  -- else
  --   msg = msg .. "请求成功：" .. err
  --   return ok, msg
  -- end

  return msg
end

return {
  send_http_request = send_http_request,
}
